#include "SpriteDirect3D.h"

namespace YoghurtGum
{

	void SpriteDirect3D::SetAlphaKey(Color a_AlphaKey)
	{
		m_AlphaKey = a_AlphaKey.GetColorD3DMCOLOR();
	}

	void SpriteDirect3D::Load(const char* a_File) 
	{ 
		// -1, +1 --- +1, +1
		//    |          |
		//    |          |
		//    |          |
		//    |          |
		// -1, -1 --- +1, -1

		/*m_BaseUpperLeft.x 	= -1.f * Device::GetWidthRatio();
		m_BaseUpperLeft.y 	=  1.f * Device::GetHeightRatio();

		m_BaseUpperRight.x 	=  1.f * Device::GetWidthRatio();
		m_BaseUpperRight.y 	=  1.f * Device::GetHeightRatio();

		m_BaseLowerLeft.x 	= -1.f * Device::GetWidthRatio();
		m_BaseLowerLeft.y 	= -1.f * Device::GetHeightRatio();

		m_BaseLowerRight.x 	=  1.f * Device::GetWidthRatio();
		m_BaseLowerRight.y 	= -1.f * Device::GetHeightRatio();*/

		/*m_BaseUpperLeft.x 	=  0.f;
		m_BaseUpperLeft.y 	=  2.f * Device::GetHeightRatio();

		m_BaseUpperRight.x 	=  2.f * Device::GetWidthRatio();
		m_BaseUpperRight.y 	=  2.f * Device::GetHeightRatio();

		m_BaseLowerLeft.x 	=  0.f;
		m_BaseLowerLeft.y 	=  0.f;

		m_BaseLowerRight.x 	=  2.f * Device::GetWidthRatio();
		m_BaseLowerRight.y 	=  0.f;*/

		m_BaseUpperLeft.x 	=  0.f;
		m_BaseUpperLeft.y 	=  0.f;

		m_BaseUpperRight.x 	=  2.f * Device::GetWidthRatio();
		m_BaseUpperRight.y 	=  0.f;

		m_BaseLowerLeft.x 	=  0.f;
		m_BaseLowerLeft.y 	= -2.f * Device::GetHeightRatio();

		m_BaseLowerRight.x 	=  2.f * Device::GetWidthRatio();
		m_BaseLowerRight.y 	= -2.f * Device::GetHeightRatio();

		strcpy(m_FileName, a_File);

		/*char finalpath[256];
		ZeroMemory(finalpath, 256);
		strcpy(finalpath, Device::GetWorkingDirectory());
		strcat(finalpath, a_File);*/

		char* path = Device::GetWorkingDirectory();

		wchar_t* wchar_path = new wchar_t[YG_MAX_PATH];
		YG_ZEROMEMORY(wchar_path, YG_MAX_PATH);
		mbstowcs(wchar_path, path, YG_MAX_PATH);

		wchar_t* wchar_file = new wchar_t[strlen(a_File) + 1];
		YG_ZEROMEMORY(wchar_file, strlen(a_File) + 1);
		mbstowcs(wchar_file, a_File, YG_MAX_PATH);

		const int length = strlen(path);
		for (int i = 0; wchar_file[i]; ++i) { wchar_path[length + i] = wchar_file[i]; }

		delete[] wchar_file;

		Log::GetSingleton() << "Loading sprite: '" << wchar_path << "'" << ENDL;

		m_Loaded = true;

		RenderMethod* method = Device::GetRenderMethod();
		LPDIRECT3DMOBILEDEVICE dev = (LPDIRECT3DMOBILEDEVICE)method->GetDevice();
		D3DMPOOL pool = *(D3DMPOOL*)method->GetPool();

		// D3DMCOLOR_XRGB(255, 0, 255)
		D3DMXIMAGE_INFO desc;
		if (ErrorDirect3D::Failed ( D3DMXCreateTextureFromFileExW(	
					dev,                // device
					wchar_path,         // path to image
					D3DMX_DEFAULT,      // width
					D3DMX_DEFAULT,      // height
					D3DMX_DEFAULT,      // miplevels
					0,                  // usage
					D3DMFMT_UNKNOWN,    // pixel format
					pool,               // pool
					D3DMX_DEFAULT,      // filtering
					D3DMX_DEFAULT,	    // mip filter
					m_AlphaKey,         // alpha key
					&desc,              // description of image
					NULL,               // palette
					&m_Texture          // texture 
				)
			)
		)
		{
			ERROR_EXPLAIN("Failed to create texture.");
			m_Loaded = false;
			return;
		}

		LPDIRECT3DMOBILESURFACE target;
		m_Texture->GetSurfaceLevel(0, &target);

		// set dimensions
		m_Width = (float)desc.Width;
		m_Height = (float)desc.Height;

		Log::GetSingleton() << "Dimensions: [" << m_Width << ", " << m_Height << "]" << ENDL;

		if (ErrorDirect3D::Failed(dev->CreateVertexBuffer(	
					6 * sizeof(Vertex), 
					0, 
					D3DMFVF_CUSTOMVERTEX, 
					*(D3DMPOOL*)method->GetPool(), 
					&m_VertexBuffer
				)
			)
		)
		{
			ERROR_EXPLAIN("Failed to create vertex buffer.");
			return;
		}

		Scale(m_Width * 1, m_Height * 1);
	}

	void SpriteDirect3D::Scale(float a_Width, float a_Height) 
	{ 
		const float widthfactor = (a_Width / Device::GetWidth()) / Device::GetWidthRatio();
		const float heightfactor = (a_Height / Device::GetHeight()) / Device::GetHeightRatio();

		Log::GetSingleton() << "Width factor: " << widthfactor << "" << ENDL;
		Log::GetSingleton() << "Height factor: " << heightfactor << "" << ENDL;

		m_UpperLeft.x = m_BaseUpperLeft.x * widthfactor;
		m_UpperLeft.y = m_BaseUpperLeft.y * heightfactor;

		m_UpperRight.x = m_BaseUpperRight.x * widthfactor;
		m_UpperRight.y = m_BaseUpperRight.y * heightfactor;

		m_LowerLeft.x = m_BaseLowerLeft.x * widthfactor;
		m_LowerLeft.y = m_BaseLowerLeft.y * heightfactor;

		m_LowerRight.x = m_BaseLowerRight.x * widthfactor;
		m_LowerRight.y = m_BaseLowerRight.y * heightfactor;
	}

	void SpriteDirect3D::Rotate(float a_Degrees) 
	{ 
		Vec2 center = Vec2((m_Width / Device::GetWidth()) * Device::GetWidthRatio(), (m_Height / Device::GetHeight()) *	Device::GetHeightRatio());
		//Vec2 factor = center.Normalize();
		//Vec2 factor = center;
		//Vec2 factor = Vec2(m_Width, m_Height);
		const float length = center.GetLength();
		const float length2 = Vec2(m_Width, m_Height).GetLength();
		Vec2 factor((length2 / Device::GetWidth()), (length2 / Device::GetHeight()));

		Vec2 centerX(0, center.y);
		// dot product = length of A times cosine delta
		const float angle = center.DotProduct(centerX) / (length * centerX.GetLength());

		/*Vec2(4, 3).Dot(Vec2(4, 0)) =
		4 * 4 + 3 * 0 = 64
		5 / 64 = 0.078125

		Vec2(32, 32).Dot(Vec2(32, 0)) =
		32 * 32 + 0 = 1024
		sqrtf(1024) = 32


		4 * 0 + 3 * 3 = 9
		9 / 5 = 

		cos(37) = 0.7986355
		cos(53) = 0.6018502*/

		/*printf("Angle: %f" << ENDL, angle);
		printf("Radians: %f" << ENDL, CosDeg(45));
		printf("" << ENDL);*/

		const float widthfactor = m_Width / Device::GetWidth();
		const float heightfactor = m_Height / Device::GetHeight();

		//printf("Debug: %.2f, %.2f, %.2f, %.2f" << ENDL, factor.x, factor.y, widthfactor, heightfactor);

		// green ----- blue
		//   |          |
		//   |          |
		//	 |          |
		//	 |          |
		//	red ----- white

		/*m_UpperRight.x	= (CosDeg(a_Degrees + 45) * factor.x);
		m_UpperRight.y	= (SinDeg(a_Degrees + 45) * factor.y);

		m_UpperLeft.x 	= (CosDeg(a_Degrees + 135) * factor.x);
		m_UpperLeft.y 	= (SinDeg(a_Degrees + 135) * factor.y);

		m_LowerLeft.x 	= (CosDeg(a_Degrees + 225) * factor.x);
		m_LowerLeft.y 	= (SinDeg(a_Degrees + 225) * factor.y);

		m_LowerRight.x	= (CosDeg(a_Degrees + 315) * factor.x);
		m_LowerRight.y	= (SinDeg(a_Degrees + 315) * factor.y);*/

		float rotation;
		if (m_Width == m_Height) { rotation = 45.f; }
		else
		{
			
			//rotation = 62.f; // 33, 63
			//rotation = 63.f; // 33, 63
			//rotation = 63.2f; // 33, 63
			//rotation = 63.3f; // 33, 64
			//rotation = 63.4f; // 33, 64
			//rotation = 63.5f; // 32, 65
			//rotation = 63.51f; // 32, 65
			rotation = 63.52f; // 32, 65
			//rotation = 63.55f; // 31, 65
			//rotation = 63.6f; // 31, 64
			//rotation = 64.f; // 31, 65
		}

		m_UpperRight.x	= (Math::CosDeg(a_Degrees +		rotation) * factor.x);
		m_UpperRight.y	= (Math::SinDeg(a_Degrees +		rotation) * factor.y);

		m_UpperLeft.x 	= (Math::CosDeg(a_Degrees + 180 - rotation) * factor.x);
		m_UpperLeft.y 	= (Math::SinDeg(a_Degrees + 180 - rotation) * factor.y);

		m_LowerLeft.x 	= (Math::CosDeg(a_Degrees + 180 + rotation) * factor.x);
		m_LowerLeft.y 	= (Math::SinDeg(a_Degrees + 180 + rotation) * factor.y);

		m_LowerRight.x	= (Math::CosDeg(a_Degrees -		rotation) * factor.x);
		m_LowerRight.y	= (Math::SinDeg(a_Degrees -		rotation) * factor.y);

		SetVertices();
	}

	void SpriteDirect3D::Draw(float a_X, float a_Y, float a_Depth) 
	{ 
		if (m_Loaded) 
		{
			LPDIRECT3DMOBILEDEVICE device = (LPDIRECT3DMOBILEDEVICE)Device::GetRenderMethod()->GetDevice();

			// alpha keying

			device->SetRenderState(D3DMRS_ALPHABLENDENABLE, TRUE);
			device->SetRenderState(D3DMRS_SRCBLEND, D3DMBLEND_SRCALPHA);
			device->SetRenderState(D3DMRS_DESTBLEND, D3DMBLEND_INVSRCALPHA);

			// TODO: Optimize

			const float width = (float)Device::GetWidth() / 2.f;
			const float height = (float)Device::GetHeight() / -2.f;

			D3DMXMATRIX position;
			D3DMXMatrixTranslation(&position, (a_X / width) - 1, (a_Y / height) + 1, 0);
			D3DMMATRIX final;

			for (int j = 0; j < 4; j++)
			{
				for (int i = 0; i < 4; i++)
				{
					final.m[j][i] = D3DM_MAKE_D3DMVALUE(position.m[j][i]);
				}
			}

			if (ErrorDirect3D::Failed(device->SetTransform(D3DMTS_WORLD, &final, D3DMFMT_D3DMVALUE_FLOAT)))
			{
				ERROR_EXPLAIN("Failed to transform matrix.");
			}

			if (ErrorDirect3D::Failed(device->SetTexture(0, m_Texture)))
			{
				ERROR_EXPLAIN("Failed to set texture.");
			}
			if (ErrorDirect3D::Failed(device->SetStreamSource(0, m_VertexBuffer, sizeof(Vertex))))
			{
				ERROR_EXPLAIN("Failed to set stream source.");
			}
			if (ErrorDirect3D::Failed(device->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 2)))
			{
				Log::GetSingleton() << "Rendering '" << m_FileName << "'" << ENDL;
				ERROR_EXPLAIN("Failed to draw primitive.");
			}

			device->SetRenderState(D3DMRS_ALPHABLENDENABLE, FALSE);
			device->SetRenderState(D3DMRS_SRCBLEND, D3DMBLEND_ONE);
			device->SetRenderState(D3DMRS_DESTBLEND, D3DMBLEND_ZERO);
		}
	}

	void SpriteDirect3D::SetVertices()
	{
		if (ErrorDirect3D::Failed(m_VertexBuffer->Lock(0, 0, (void**)&m_Vertices, 0)))
		{
			ERROR_EXPLAIN("Failed to lock vertex buffer.");
			return;
		}

		// LL
		m_Vertices[0].x = m_LowerLeft.x;
		m_Vertices[0].y = m_LowerLeft.y;
		m_Vertices[0].z = 0.f;
		m_Vertices[0].color = 0xFFFFFFFF;
		m_Vertices[0].tu = 0.f;
		m_Vertices[0].tv = 1.f;

		// UL
		m_Vertices[1].x = m_UpperLeft.x;
		m_Vertices[1].y = m_UpperLeft.y;
		m_Vertices[1].z = 0.f;
		m_Vertices[1].color = 0xFFFFFFFF;
		m_Vertices[1].tu = 0.f;
		m_Vertices[1].tv = 0.f;

		// UR
		m_Vertices[2].x = m_UpperRight.x;
		m_Vertices[2].y = m_UpperRight.y;
		m_Vertices[2].z = 0.f;
		m_Vertices[2].color = 0xFFFFFFFF;
		m_Vertices[2].tu = 1.f;
		m_Vertices[2].tv = 0.f;

		// UR
		m_Vertices[3].x = m_UpperRight.x;
		m_Vertices[3].y = m_UpperRight.y;
		m_Vertices[3].z = 0.f;
		m_Vertices[3].color = 0xFFFFFFFF;
		m_Vertices[3].tu = 1.f;
		m_Vertices[3].tv = 0.f;

		// LL
		m_Vertices[4].x = m_LowerLeft.x;
		m_Vertices[4].y = m_LowerLeft.y;
		m_Vertices[4].z = 0.f;
		m_Vertices[4].color = 0xFFFFFFFF;
		m_Vertices[4].tu = 0.f;
		m_Vertices[4].tv = 1.f;

		// LR
		m_Vertices[5].x = m_LowerRight.x;
		m_Vertices[5].y = m_LowerRight.y;
		m_Vertices[5].z = 0.f;
		m_Vertices[5].color = 0xFFFFFFFF;
		m_Vertices[5].tu = 1.f;
		m_Vertices[5].tv = 1.f;

		//memcpy(mem_vertices, m_Vertices, 6 * sizeof(Vertex));
		m_VertexBuffer->Unlock();
	}

}; // namespace YoghurtGum